#include "RageSoundDriver_Wii.h"
#include "RageLog.h"
#include "RageSoundManager.h"

#include <asndlib.h>
#include <unistd.h>
#include <malloc.h>

static RageSoundDriver_Wii::wiivoice mVoices[MAX_SND_VOICES];
static bool gBufferThreadRunning;

static void SoundFillBuffer(s32 voice) {
	ASSERT(voice < MAX_SND_VOICES);
	
	RageSoundDriver_Wii::wiivoice &v = mVoices[voice];
	// ensure the voice is valid
	if(!v.active || (v.eof && !v.bufferReady)) {
		ASND_StopVoice(voice);
		v.active = false;
		return;
	}
	
	if(!v.bufferReady) {
		return;
	}
	
	v.activeBuffer ^= 1;
	v.position += v.bufferCount;
	v.bufferReady = false;
	
	printf("Got next buffer (%d)", v.activeBuffer);
	
	ASND_AddVoice(voice, v.buffer[v.activeBuffer], SND_BUFFER_SIZE);
}

static int SoundBufferThread(void *p) {
	while(gBufferThreadRunning) {
		for(int i = 0; i < MAX_SND_VOICES; i++) {
			RageSoundDriver_Wii::wiivoice &voice = mVoices[i];
			
			if(!voice.active || voice.eof || voice.bufferReady)
				continue;
			
			void *buffer = voice.buffer[voice.activeBuffer ^ 1];
			
			u32 got = voice.sound->GetPCM(buffer, SND_BUFFER_SIZE, 0);
			if(got < SND_BUFFER_SIZE) {
				voice.eof = true;
				((RageSound*)voice.sound)->StopPlaying();
			}
			voice.bufferCount = got;
			voice.bufferReady = true;
			
			printf("Filled next buffer (%d)", voice.activeBuffer ^ 1);
		}
		
		usleep(0);
	}
	
	return 0;
}

RageSoundDriver_Wii::RageSoundDriver_Wii() : mMutex("RageSoundDriver_Wii") {
	ASND_Init();
	ASND_Pause(0);
	
	for(int i = 0; i < MAX_SND_VOICES; i++) {
		mVoices[i].active = false;
		mVoices[i].eof = false;
		mVoices[i].position = 0;
		mVoices[i].sound = NULL;
		mVoices[i].buffer[0] = memalign(32, SND_BUFFER_SIZE);
		mVoices[i].buffer[1] = memalign(32, SND_BUFFER_SIZE);
	}
	
	mBufferThread.SetName("Sound Buffer Thread");
	gBufferThreadRunning = true;
	mBufferThread.Create(SoundBufferThread, this);
}

RageSoundDriver_Wii::~RageSoundDriver_Wii() {
	gBufferThreadRunning = false;
	ASND_End();
}

void RageSoundDriver_Wii::StartMixing(RageSoundBase *snd) {
	LOG->Trace("StartMixing(%p)", snd);
	mMutex.Lock();
	
	// find an unused voice
	int voice = ASND_GetFirstUnusedVoice();
	
	// no voices are available for the sound
	if(voice == SND_INVALID) {
		LOG->Info("Unable to find a free voice channel for sound");
		mMutex.Unlock();
		return;
	}
	
	printf("Free voice: %d", voice);
	
	wiivoice &v = mVoices[voice];
	v.mutex.Lock();
	
	// reset wiivoice
	v.active = true;
	v.eof = false;
	v.position = 0;
	v.sound = snd;
	v.activeBuffer = 0;
	v.bufferReady = false;
	v.bufferCount = 0;
	//memset(mVoices[voice].buffer, 0, SND_BUFFER_SIZE);
	
	// prebuffer
	u32 got = snd->GetPCM(v.buffer[v.activeBuffer], SND_BUFFER_SIZE, 0);
	if(got < SND_BUFFER_SIZE) {
		printf("Prebuffer EOF");
		v.eof = true;
		((RageSound*)snd)->StopPlaying();
	}
	v.position = got;
	
	ASND_PauseVoice(voice, 0);
	ASND_SetVoice(voice, VOICE_STEREO_16BIT, 48000, 0, v.buffer[v.activeBuffer], SND_BUFFER_SIZE, 192, 192, SoundFillBuffer);
	
	v.mutex.Unlock();
	mMutex.Unlock();
}

void RageSoundDriver_Wii::StopMixing(RageSoundBase *snd) {
	for(int v = 0; v < MAX_SND_VOICES; v++) {
		mVoices[v].mutex.Lock();
		if(mVoices[v].active && mVoices[v].sound == snd) {
			LOG->Trace("Stopping voice %d", v);
			ASND_StopVoice(v);
			mVoices[v].active = false;
			mVoices[v].mutex.Unlock();
			return;
		}
		mVoices[v].mutex.Unlock();
	}
}

int64_t RageSoundDriver_Wii::GetPosition(const RageSoundBase *snd) const {
	for(int v = 0; v < MAX_SND_VOICES; v++) {
		if(mVoices[v].sound != snd) {
			continue;
		}
		
		if(!mVoices[v].active)
			return 0;
		
		return mVoices[v].position / 4;
	}
	
	return 0;
}

